# __author__ = 'heyin'
# __date__ = '2018/12/25 10:22'
# 单独测试策略使用
import os
import time
import json
import pandas as pd
import numpy as np
import requests


def timestamp2date(timestamp):
    if timestamp < 0:  # 小于1970.1.1的时间不清楚如何处理
        timestamp = 0
    time_array = time.localtime(timestamp)
    date = time.strftime("%Y-%m-%d", time_array)  # 字符串
    return date


class MaStrategy(object):
    """单均线策略，如果增加其他策略，应当将下载，读取文件，收益计算等提取出一个公共类，每个算法再有一个类，继承公共类"""

    @staticmethod
    def download(stockcode):
        """从雅虎股票获取数据"""
        # 构造首次请求url，用以获取首次交易的时间
        first_url = 'https://query1.finance.yahoo.com/v8/finance/chart/{}?symbol={}&interval=1d'.format(stockcode,
                                                                                                        stockcode)
        # print(first_url)
        first_resp = requests.get(first_url).content.decode('utf-8')

        if json.loads(first_resp).get('chart').get('result') is None:
            error = json.loads(first_resp).get('chart').get('error').get('description')
            return error
        first_trade_date = json.loads(first_resp).get('chart').get('result')[0].get('meta').get('firstTradeDate')
        # 构造请求所有数据的url
        all_url = 'https://query1.finance.yahoo.com/v8/finance/chart/{}?symbol={}&period1={}&period2={}&interval=1d'.format(
            stockcode, stockcode, first_trade_date, int(time.time()))
        # print(all_url)
        print('发起%s的数据请求' % stockcode)
        all_resp = requests.get(all_url).content.decode('utf-8')

        result = json.loads(all_resp).get('chart').get('result')[0]

        # meta = result.get('meta')
        timestamp = result.get('timestamp')  # 从北京时间 1971-2-5 22:30 开始  到现今每天的同一时刻。注意此为开盘时间
        quote = result.get('indicators').get('quote')[0]

        # 时间戳转为具体日期
        date_ = list()
        for t in timestamp:
            # print(t)
            date_.append(timestamp2date(t))

        high = quote.get('high')
        low = quote.get('low')
        open_ = quote.get('open')
        close = quote.get('close')
        volume = quote.get('volume')  # 2462430000  24亿  写入原始数据，需要的时候自行处理

        day_data = np.array([open_, close, low, high], dtype=np.float64)
        # 将None替换为nan
        day_data[day_data == None] = np.nan

        day_data = day_data.T
        day_data = np.around(day_data, 3)

        date_ = np.array(date_).reshape((day_data.shape[0], 1))
        volume = np.array(volume).reshape((day_data.shape[0], 1))
        day_data = np.hstack((np.array(date_), day_data, np.array(volume)))
        stock_data = pd.DataFrame(day_data, columns=['date', 'open', 'close', 'low', 'high', 'volume'])
        stock_data.to_csv('./stockdata/%s.csv' % stockcode, header=True, index=False)
        # return stock_data  # 返回df

    @staticmethod
    def read_local_csv(stockcode):
        """从本地文件读取数据返回df"""
        stock_data = pd.read_csv('./stockdata/%s.csv' % stockcode, parse_dates=[1])
        return stock_data

    @staticmethod
    def download_or_read(stockcode):
        """判断本地是否有此股票的文件数据"""
        if os.path.exists('./stockdata/%s.csv' % stockcode):
            return True
        else:
            return False

    @staticmethod
    def mov_avg(stockdata, ma):
        stockdata['ma_' + str(ma)] = stockdata['close'].rolling(ma).mean().round(2)
        return stockdata

    @staticmethod
    def strategy(stock_data, start_date, ma):
        """上穿某个均线买入，跌穿某个均线卖出"""
        ma = 'ma_%s' % ma
        stock_data = stock_data.set_index('date', drop=True).loc[start_date:, ['open', 'close', ma]]
        # print(stock_data)
        isbuy = False  # 是否买入
        yes_dopen, yes_close, yes_date, yes_ma = None, None, None, None  # 变量保存昨日信息，用以均线比较计算
        buy_price = None  # 买入价
        shouyi_list = list()
        item = dict()
        for index, data in stock_data.iterrows():
            _ma = data[ma]  # 当日均线值
            dopen = data['open']
            close = data['close']
            # print(type(yes_ma), type(_ma), type(yes_close))
            if all([yes_dopen, yes_close, yes_date, yes_ma]):
                if yes_close < yes_ma and close > _ma and isbuy is False:
                    # print(index, dopen, close, _ma, '上穿%s均线，买入, 买入价%s' % (ma, close))
                    buy_price = close
                    isbuy = True
                    item['buy_msg'] = '%s上穿%s均线，买入, 买入价%s' % (index, ma, round(close, 3))
                elif yes_close > yes_ma and close < _ma and isbuy is True:
                    diff_price = round((close - buy_price) / buy_price, 2)
                    # print(index, dopen, close, _ma, '下穿%s均线，卖出, 卖出价%s，收益率%s%%' % (ma, close, diff_price * 100))
                    isbuy = False
                    item['sell_msg'] = '%s下穿%s均线，卖出, 卖出价%s，收益率%s%%' % (
                        index, ma, round(close, 3), round(diff_price * 100, 3))
                    item['return_rate'] = diff_price
                    shouyi_list.append(item)
                    item = dict()
            yes_dopen, yes_close, yes_date, yes_ma = dopen, close, index, _ma
        return shouyi_list  # 返回每次交易的涨跌幅度的列表

    @staticmethod
    def shouyi(shouyi: list, money):
        """通过计算得到对应本金的收益情况"""
        j = money
        for i in shouyi:
            money = money * (1 + i.get('return_rate'))
        return round(money - j, 2), round(money, 2), '%s%%' % round((money - j) / j * 100)

    @staticmethod
    def run(stockcode, ma, startdate, money):
        """运行主逻辑处理"""
        # stockcode = 'AAPL'
        if not MaStrategy.download_or_read(stockcode):  # 本地文件不存在
            stockdata = MaStrategy.download(stockcode)  # 下载数据
            if isinstance(stockdata, str):
                print(stockdata)  # 股票代号错误，输出信息
                return {'error': stockdata}
        # 均从本地读取文件数据，否则网络下载数据会报str和float无法进行计算
        stockdata = MaStrategy.read_local_csv(stockcode)
        # 计算指定均线，返回值中数据已带有均线数据
        stockdata = MaStrategy.mov_avg(stockdata, ma)
        # print(stockdata)
        # 计算每次交易的盈亏百分比
        shouyi_list = MaStrategy.strategy(stockdata, startdate, ma)

        # 计算总收益
        income = MaStrategy.shouyi(shouyi_list, money)  # (不含本金，含本金)
        # print(shouyi_list)
        now_year = int(timestamp2date(time.time()).split('-')[0])
        start_year = int(startdate.split('-')[0])
        item = dict()
        item['sum_rate'] = '根据均线 %s 计算的 %s %s 年收益率为%s' % (ma, stockcode, now_year - start_year, income[2])
        item['ex_msg'] = shouyi_list  # 交易信息
        # print(shouyi_list)
        win = 0  # 胜利次数
        loss = 0  # 失败次数
        for i in shouyi_list:
            if i['return_rate'] > 0:
                win += 1
            else:
                loss += 1
        item['win_rate'] = '胜率%s' % round(win / len(shouyi_list), 2)
        item['win'] = win
        item['loss'] = loss
        return item


if __name__ == '__main__':
    ma_list = [5, 10, 20, 30, 40, 50, 60, 70, 80, 100]
    start_date = ['2014-12-01']
    for date in start_date:
        for ma in ma_list:
            item = MaStrategy().run('BTC-USD', ma, date, 10000)
            print(item)

# KDJ()
